import type { State } from '@/store';
import { SETTINGSKEY, settingsStorage, defaultState, Status, PersistenMode } from '@/store';
import {
  GlobalData,
  globalStorageKey,
  ScopeData,
  scopeStorageKey,
} from './content/hooks/persistent';
import { getXPath } from '@/lib/utils';

async function getSettings(): Promise<State | null> {
  const settings = localStorage.getItem(SETTINGSKEY);
  if (settings) {
    return JSON.parse(settings) as State;
  }
  const extensionSettings = await settingsStorage.getValue();
  if (extensionSettings && extensionSettings.state) {
    return extensionSettings.state;
  }
  // 如果 extensionSettings 都没有值,表示配置没有被改动过,那么就使用默认值
  return defaultState;
}

export type List = Array<{
  xpath: string;
  text: string;
  expression: XPathExpression;
  isMatch: boolean;
}>;

function createXpathList(xpathData: Record<string, string>) {
  const list: List = [];
  const evaluator = new XPathEvaluator();
  Object.keys(xpathData).forEach((key) => {
    list.push({
      xpath: key,
      text: xpathData[key],
      expression: evaluator.createExpression(key),
      isMatch: false,
    });
  });
  return list;
}

function replace(xpathList: List) {
  const list = xpathList.filter((list) => {
    return !list.isMatch;
  });
  list.forEach((xpath) => {
    const result = xpath.expression.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE);
    if (result && result.singleNodeValue) {
      const target = result.singleNodeValue;
      target.textContent = xpath.text;
      xpath.isMatch = true;
    }
  });
}

function needObserve(target: Node) {
  if (target.nodeType === 1) {
    const tagName = (target as HTMLElement).tagName.toLowerCase();
    return !['html', 'body', 'head', 'meta', 'script', 'style', 'title'].includes(tagName);
  }
  return false;
}

function replaceByXpath(xpathList: List, xpath: string, target: HTMLElement) {
  const index = xpathList.findIndex((item) => {
    return item.xpath === xpath;
  });
  if (index !== -1) {
    target.textContent = xpathList[index].text;
  }
}

export default defineContentScript({
  matches: ['<all_urls>'],
  runAt: 'document_start',
  async main(ctx) {
    const settings = await getSettings();
    if (
      settings &&
      settings.status === Status.RUNNING &&
      settings.persistentSettings.needPersistent
    ) {
      const href: string = location.href;

      const persistentSettings = settings.persistentSettings;

      // 全局模式下的替换
      if (persistentSettings.currentMode === PersistenMode.GlobalMode) {
        const data = localStorage.getItem(globalStorageKey);
        if (data) {
          const global: GlobalData = JSON.parse(data) as GlobalData;
          if (global[href]) {
            document.write(global[href]);
            document.close();
          }
        }
      }

      // 轮询模式下的替换
      if (persistentSettings.currentMode === PersistenMode.RollPollingMode) {
        const data = localStorage.getItem(scopeStorageKey);
        if (data) {
          const scope: ScopeData = JSON.parse(data) as ScopeData;
          if (scope[href]) {
            const list = createXpathList(scope[href]);
            let isTimeout = false;
            ctx.setInterval(() => {
              if (!isTimeout) {
                replace(list);
              }
            }, persistentSettings.rollPolling.interval);
            ctx.setTimeout(() => {
              isTimeout = true;
            }, persistentSettings.rollPolling.duration);
          }
        }
      }

      // 无闪烁模式
      if (persistentSettings.currentMode === PersistenMode.FlickerFreeMode) {
        const data = localStorage.getItem(scopeStorageKey);
        if (data) {
          const scope: ScopeData = JSON.parse(data) as ScopeData;
          if (scope[href]) {
            const list = createXpathList(scope[href]);
            const observer = new MutationObserver(function (mutations) {
              mutations.forEach(function (mutation) {
                const target = mutation.target;
                if (needObserve(target)) {
                  const xpath = getXPath(target as HTMLElement);
                  if (xpath) {
                    replaceByXpath(list, xpath, target as HTMLElement);
                  }
                }
              });
            });
            observer.observe(document, {
              subtree: true,
              childList: true,
            });
            ctx.setTimeout(() => {
              observer.disconnect();
            }, persistentSettings.flickerFree.duration);
          }
        }
      }
    }
  },
});
